package br.iars.web.bean;

import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import br.iars.service.v01.recommendationservice.RecommendationService;
import br.iars.service.v01.recommendationservice.RecommendationService_Service;
import br.iars.service.v01.recommendationservice.exception.RecommendationServiceException;
import br.iars.service.v01.recommendationservice.type.Context;
import br.iars.service.v01.recommendationservice.type.ElementList;
import br.iars.service.v01.recommendationservice.type.PreferenceItem;
import br.iars.service.v01.recommendationservice.type.RecommendRequest;
import br.iars.service.v01.recommendationservice.type.RecommendedItem;
import br.iars.service.v01.recommendationservice.type.SetPreferencesRequest;
import br.iars.web.exception.SystemException;
import br.iars.web.model.api.ContextModel;
import br.iars.web.model.api.ItemModel;
import br.iars.web.model.api.UserModel;
import br.iars.web.model.entity.Item;
import br.iars.web.model.entity.Preference;
import br.iars.web.model.entity.PreferenceType;
import br.iars.web.model.entity.User;
import br.iars.web.model.impl.ContextModelImpl;
import br.iars.web.model.impl.ItemModelImpl;
import br.iars.web.model.impl.UserModelImpl;
import br.iars.web.util.Constants;

public class RecommenderBean extends GenericBean {

	private static final long serialVersionUID = -594375081069647862L;

	private transient Logger logger;

	private boolean showSearchUser;

	private UserModel userModel;
	private ContextModel contextModel;
	private ItemModel itemModel;
	private RecommendationService_Service service;
	private RecommendationService recommendationService;

	private String userEmail;
	private List<Preference> moviesPreferences;
	private List<Preference> singersPreferences;
	private List<Preference> booksPreferences;

	private static final int PREFERENCES_NUMBER = 10;

	private static final int RECOMMENDATION_NUMBER = 10;

	private br.iars.web.model.entity.Context moviesContext;

	private br.iars.web.model.entity.Context singersContext;

	private br.iars.web.model.entity.Context booksContext;

	private List<Preference> moviesOnlyOwnContext;

	private List<Preference> moviesWithAllContext;

	private List<Preference> singersOnlyOwnContext;

	private List<Preference> singersWithAllContext;

	private List<Preference> booksOnlyOwnContext;

	private List<Preference> booksWithAllContext;

	public RecommenderBean() throws SystemException {
		super();
		this.logger = Logger.getLogger(getClass());
		this.userModel = new UserModelImpl();
		this.contextModel = new ContextModelImpl();
		this.itemModel = new ItemModelImpl();

		try {
			service = new RecommendationService_Service(new URL("http://localhost:8080/iars-service-0.0.1-SNAPSHOT/RecommendationServiceImpl?wsdl"));
			recommendationService = service.getRecommendationServicePort();
		} catch (Exception e) {
			logger.error("Erro ao instanciar o client do serviço de recomendação", e);
			throw new SystemException(e);
		}

		init();
	}

	public void init() {
		Preference preference = null;

		showSearchUser = true;

		moviesContext = contextModel.findByID(Constants.CONTEXT_MOVIES_ID);
		List<Item> movies = moviesContext.getItens();

		singersContext = contextModel.findByID(Constants.CONTEXT_SINGERS_ID);
		List<Item> singers = singersContext.getItens();

		booksContext = contextModel.findByID(Constants.CONTEXT_BOOKS_ID);
		List<Item> books = booksContext.getItens();

		moviesPreferences = new LinkedList<Preference>();
		singersPreferences = new LinkedList<Preference>();
		booksPreferences = new LinkedList<Preference>();

		for (Item movie : movies) {
			preference = new Preference(movie, moviesContext);
			moviesPreferences.add(preference);
		}

		for (Item singer : singers) {
			preference = new Preference(singer, singersContext);
			singersPreferences.add(preference);
		}

		for (Item book : books) {
			preference = new Preference(book, booksContext);
			booksPreferences.add(preference);
		}
	}

	public List<Preference> getMoviesOnlyOwnContext() {
		return moviesOnlyOwnContext;
	}

	public void setMoviesOnlyOwnContext(List<Preference> moviesOnlyOwnContext) {
		this.moviesOnlyOwnContext = moviesOnlyOwnContext;
	}

	public List<Preference> getMoviesWithAllContext() {
		return moviesWithAllContext;
	}

	public void setMoviesWithAllContext(List<Preference> moviesWithAllContext) {
		this.moviesWithAllContext = moviesWithAllContext;
	}

	public List<Preference> getSingersOnlyOwnContext() {
		return singersOnlyOwnContext;
	}

	public void setSingersOnlyOwnContext(List<Preference> singersOnlyOwnContext) {
		this.singersOnlyOwnContext = singersOnlyOwnContext;
	}

	public List<Preference> getSingersWithAllContext() {
		return singersWithAllContext;
	}

	public void setSingersWithAllContext(List<Preference> singersWithAllContext) {
		this.singersWithAllContext = singersWithAllContext;
	}

	public List<Preference> getBooksOnlyOwnContext() {
		return booksOnlyOwnContext;
	}

	public void setBooksOnlyOwnContext(List<Preference> booksOnlyOwnContext) {
		this.booksOnlyOwnContext = booksOnlyOwnContext;
	}

	public List<Preference> getBooksWithAllContext() {
		return booksWithAllContext;
	}

	public void setBooksWithAllContext(List<Preference> booksWithAllContext) {
		this.booksWithAllContext = booksWithAllContext;
	}

	public String getUserEmail() {
		return userEmail;
	}

	public void setUserEmail(String userEmail) {
		this.userEmail = userEmail;
	}

	public List<Preference> getMoviesPreferences() {
		return moviesPreferences;
	}

	public void setMoviesPreferences(List<Preference> moviesPreferences) {
		this.moviesPreferences = moviesPreferences;
	}

	public List<Preference> getSingersPreferences() {
		return singersPreferences;
	}

	public void setSingersPreferences(List<Preference> singersPreferences) {
		this.singersPreferences = singersPreferences;
	}

	public List<Preference> getBooksPreferences() {
		return booksPreferences;
	}

	public void setBooksPreferences(List<Preference> booksPreferences) {
		this.booksPreferences = booksPreferences;
	}
	
	public boolean isShowSearchUser() {
		return showSearchUser;
	}

	public void setShowSearchUser(boolean showSearchUser) {
		this.showSearchUser = showSearchUser;
	}

	public String save() {
		showSearchUser = false;
		if (userModel.findByEmail(this.userEmail) != null) {
			addMessageError(MESSAGE_FIELD_NAME, "user_validation_email_already_in_use");
		} else {
			Map<String, Object> messages = new HashMap<String, Object>();

			ElementList<PreferenceItem> moviesPreferencesRated = loadPreferencesRated(moviesContext, moviesPreferences, messages);
			ElementList<PreferenceItem> booksPreferencesRated = loadPreferencesRated(booksContext, booksPreferences, messages);
			ElementList<PreferenceItem> singersPreferencesRated = loadPreferencesRated(singersContext, singersPreferences, messages);

			if (moviesPreferencesRated.getItens().size() != PREFERENCES_NUMBER) {
				messages.put("preference_validation_movies_should_be_rated", PREFERENCES_NUMBER);
			}
			if (singersPreferencesRated.getItens().size() != PREFERENCES_NUMBER) {
				messages.put("preference_validation_singers_should_be_rated", PREFERENCES_NUMBER);
			}
			if (booksPreferencesRated.getItens().size() != PREFERENCES_NUMBER) {
				messages.put("preference_validation_books_should_be_rated", PREFERENCES_NUMBER);
			}

			if (messages.isEmpty()) {
				SetPreferencesRequest movies = loadSetPreferencesRequest("Movies", "Movies", moviesPreferencesRated);
				SetPreferencesRequest singers = loadSetPreferencesRequest("Singers", "Singers", singersPreferencesRated);
				SetPreferencesRequest books = loadSetPreferencesRequest("Books", "Books", booksPreferencesRated);

				try {
					User user = new User(userEmail);
					userModel.insert(user);

					recommendationService.setPreferences(movies);
					recommendationService.setPreferences(singers);
					recommendationService.setPreferences(books);

					loadRecommendations(user);

					addMessageSuccess(MESSAGE_FIELD_NAME, "preference_save_success");
					return "listUserRecommendations";
				} catch (RecommendationServiceException e) {
					addMessageError(MESSAGE_FIELD_NAME, "recommendation_load_error");
				} catch (Exception e) {
					addMessageError(MESSAGE_FIELD_NAME, "preference_save_error");
				}

			} else {
				addMessagesError(MESSAGE_FIELD_NAME, messages);
			}
		}
		return "";
	}

	public void loadRecommendations() {
		showSearchUser = true;
		try {
			loadRecommendations(new User(this.userEmail));
		} catch (RecommendationServiceException e) {
			addMessageError(MESSAGE_FIELD_NAME, "recommendation_load_error");
		}
	}

	private void loadRecommendations(User user) throws RecommendationServiceException {

		/*
		 * Movies recommendations
		 */
		moviesWithAllContext = loadRecommendations(user, moviesContext, false);
		moviesOnlyOwnContext = loadRecommendations(user, moviesContext, true);

		/*
		 * Singers recommendations
		 */
		singersWithAllContext = loadRecommendations(user, singersContext, false);
		singersOnlyOwnContext = loadRecommendations(user, singersContext, true);

		/*
		 * Books recommendations
		 */
		booksWithAllContext = loadRecommendations(user, booksContext, false);
		booksOnlyOwnContext = loadRecommendations(user, booksContext, true);

	}

	@SuppressWarnings("unchecked")
	private List<Preference> loadRecommendations(User user, br.iars.web.model.entity.Context context, boolean isOnlyOwnContext)
			throws RecommendationServiceException {
		Context c = new Context();
		c.setCompany(context.getCompanyName());
		c.setSystem(context.getSystemName());

		RecommendRequest request = new RecommendRequest();
		request.setUserEmail(user.getEmail());
		request.setContext(c);
		request.setOnlyOwnContext(isOnlyOwnContext);
		request.setQuantity(RECOMMENDATION_NUMBER);
		ElementList<RecommendedItem> recommendationsTemp = (ElementList<RecommendedItem>) recommendationService.recommend(request);

		Preference p = null;
		List<Preference> recommendations = new LinkedList<Preference>();
		for (RecommendedItem r : recommendationsTemp.getItens()) {
			p = new Preference();
			p.setRate((double)r.getRate());
			p.setContext(context);
			p.setItem(itemModel.findByID(r.getItemId()));
			
			recommendations.add(p);
		}

		return recommendations;
	}

	public String back() {
		init();
		return "listUserPreferences";
	}

	private ElementList<PreferenceItem> loadPreferencesRated(br.iars.web.model.entity.Context context, List<Preference> preferences,
			Map<String, Object> messages) {
		PreferenceItem preferenceItem = null;
		ElementList<PreferenceItem> preferencesRated = new ElementList<PreferenceItem>();
		boolean hasInvalidRate = false;

		for (Preference p : preferences) {
			if (p.getRate() == 0F) {
				continue;
			}

			if (p.getRate() > 0 && context.getMinRate() <= p.getRate() && p.getRate() <= context.getMaxRate()) {
				if (context.getPreferenceType().equals(PreferenceType.INTEGER)) {
					if (p.getRate().intValue() != p.getRate()) {
						hasInvalidRate = true;
						continue;
					}
				}

				preferenceItem = new PreferenceItem();
				preferenceItem.setUserEmail(this.userEmail);
				preferenceItem.setItemId(p.getItem().getId());
				preferenceItem.setRate(p.getRate().floatValue());
				preferencesRated.getItens().add(preferenceItem);
			} else {
				hasInvalidRate = true;
			}
		}

		if (hasInvalidRate) {
			Object[] args = new Object[4];
			args[0] = context.getSystemName();
			args[1] = context.getPreferenceType().toString();
			args[2] = context.getMinRate();
			args[3] = context.getMaxRate();
			messages.put("preference_validation_invalid_rate", args);
		}

		return preferencesRated;
	}

	private SetPreferencesRequest loadSetPreferencesRequest(String company, String system, ElementList<PreferenceItem> preferencesRated) {
		Context context = new Context();
		context.setCompany(company);
		context.setSystem(system);

		SetPreferencesRequest request = new SetPreferencesRequest();
		request.setContext(context);
		request.setPreferences(preferencesRated);

		return request;
	}

}
